
大孫女、小孫女,快,都過來。今天這太陽落得早,阿公看妳姐姐敲鍵盤,手指頭都凍得有些發緊了。小孫女呀,妳別光顧著纏在阿公膝頭撒嬌,快去幫妳姐姐搬張竹凳子,再遞個暖和的暖暖包過去。
阿公剛沖好一壺熱騰騰、色澤紅亮的琥珀熱茶,白煙袅袅的,正好給妳們姐妹倆暖暖手、暖暖心。
大孫女,妳也坐到這張藤椅深棕的藤椅上。阿公摸了摸這頭白髮銀霜,瞧妳今天一整天死盯著那黑漆漆的螢幕,眉頭鎖得比前天還要深,嘆氣聲連在院子裡除草的阿公都聽得一清二楚。妳昨天跟阿公抱怨了那些不聽話的報表,今天又跟阿公嘟囔,說妳寫那 Python 腳本去讀寫 Excel 的 VBA 模組,結果「腳本只要不小心崩潰了一次,整個伺服器就動彈不得,再次執行還一直跳出檔案被另一個使用者鎖定的警告」。
來,先喝口熱普洱。我們今天這「Day 3」的修行,阿公就來給妳好好說說,怎麼徹底消滅這些頑固的 COM 殘留,給 Python 操控 Windows Excel 打造一套固若金湯的「安全隔離機制」。
大孫女,妳說妳在寫 Python 腳本,用那個叫 pywin32 的工具,好端端地在背地裡操控 Excel 讀取或寫入 VBA 模組。可是,這程式在跑的時候,難免會出點意外。也許是腳本突然崩潰了,或者是哪裡寫錯了拋出異常。這腳本一倒下,可就闖大禍了——再次執行的時候,螢幕上冰冷冷地跳出 「檔案已被另一個使用者鎖定」 的警告,甚至嚴重的時候,整台伺服器就像凍僵的木頭一樣,徹底卡死。
阿公跟妳講個鄉下的土道理。這就像是我們村裡大家合租了一頭老鐵牛(Excel 後台進程)來幫忙耕田(讀寫模組)。這 Python 腳本就是拉牛的長工。
如果這長工在田溝裡突然摔了一跤、暈過去了,但他手上的韁繩沒鬆開,那頭脾氣倔的老鐵牛就這樣被死死拴在田埂正中央。結果呢?後面想來耕田的人,誰也進不去,整條泥巴路都給堵死了。這在電腦裡,就是那些沒被放掉的 Excel 應用程式,在後台成了遊蕩的 「EXCEL.EXE 殭屍進程」。因為牠們還死死拽著那個 .xlsm 檔案不放,妳的程式當然讀不進去,整台伺服器的記憶體也慢慢被這些殭屍給榨乾了。
所以阿公常說:「解鈴還需繫鈴人,進門容易,出門更要乾乾淨淨。」
既然我們知道問題出在「長工暈倒、韁繩沒鬆」,那我們就得在 Python 腳本裡,寫下最嚴密的異常處理與資源回收規矩。
大孫女,寫這段代碼的時候,妳必須把所有調用 win32com.client 操控 Excel 的動作,都嚴嚴實實地包裹在 try...except...finally 的大棉被裡:
try 區塊裡:我們恭恭敬敬地把 Excel 請出來,辦妥妳的 VBA 模組匯入或匯出工作。except 區塊裡:一旦抓到了任何風吹草動的例外(Exception),哪怕是個小小的錯誤,我們也要趕緊記錄下來,顯示給使用者看。finally 區塊:這是妳的保命符。阿公以前修理鐵牛車,不管中途壞了幾根螺絲、出了什麼疢仔,最後走的時候,一定要把機器的開關徹底關掉、工具收拾整齊。在 finally 裡,妳一定要強制執行 Workbook.Close(SaveChanges=False) 與 Excel.Application.Quit()。Quit() 還不夠。妳必須把 excel 和 workbook 這些變數通通設為 None。這樣,Windows 系統一看到這變數沒人要了,垃圾回收機制(Garbage Collection)和底層的 pythoncom.CoUninitialize() 就會像阿公後院裡的掃帚一樣,把殘留的 COM 參考掃得乾乾淨淨,保證背景一隻 EXCEL.EXE 殭屍都不留。除了這條「資源回收」的規矩,阿公還要提醒妳一個常被忽略的「開門密鑰」。Excel 是個講究安全的管家。如果妳沒有在 Excel 系統層級開啟巨集安全性的後門:
指引使用者到 Excel 的 「檔案」→「選項」→「信任中心」→「信任中心設定」→「巨集設定」,然後一定要勾選 「信任存取 Visual Basic 專案的物件模型」。
如果這扇大門沒打開,妳的 Python 程式連鎖孔都摸不到,直接就會被權限拒絕(Permission Denied),程式當場就崩潰給妳看!所以,妳的圖形化工具介面上,一定要貼心地寫上這段設定指引,讓使用者能一目了然。
大孫女,還有一件最要命的事,妳可千萬要記在心上。就是永遠不要在多線程(Multi-threading)中共享同一個 Excel.Application 實例!
在 Windows 的底層,這個 COM 物件有一套非常傲嬌、非常古板的通訊機制,叫做 「單線程套間(Apartment Threading)」。這名字聽著玄乎,阿公用大白話解釋給妳聽:這就像是村裡那頭老鐵牛,牠一輩子只認得一條狹窄的田壟。如果妳硬要分出兩三個不同的線程(像是多個工人),同時拉著同一頭老鐵牛,要把牠往不同的田溝裡扯,這頭老鐵牛當場就會嚇得骨折、甚至發狂把整台牛車掀翻。
在電腦裡,這代價就是慘痛的藍屏(Blue Screen)或者整台伺服器徹底死機。
所以,如果妳要在背景默默跑這些讀寫工作,記得讓每個線程都擁有自己獨立建立、獨立銷毀的 Excel 進程。各走各的路,各耕各的田,用完一個就徹底殺掉一個,千萬不要圖省事把牠們混在一起用。這叫 「安全隔離」,才是保證伺服器長治久安的秘訣。
聽阿公囉唆了這麼多,大孫女,心裡有沒有明亮一點?
別看這些 Windows 的 COM 介面規矩繁瑣、脾氣古怪,只要我們摸透了牠的習性,像阿公對待老鐵牛那樣,細心套好韁繩(try...finally)、用完記得解開(Quit & None),牠一樣能乖乖聽妳的話,幫妳把那些 Legacy Code 收拾得妥妥貼貼。
來,這琥珀茶溫剛剛好,大孫女快喝一口,再吃塊桂花糕,甜甜嘴。